
#pragma once

#include <cassert>
#include <iostream>
#include "SceneRenderer.h"
#include "Scene.h"
#include "Camera.h"
#include "OgreMath/OgreColourValue.h"
#include "OgreMath/OgreMath.h"

class BruteForceRenderer : public SceneRenderer
{
private:
	Surface* rayIntersectUpdate(Ray::Intersection &intersection, const Ray &ray, const Scene &scene, int recursionLevel) const
	{
		Surface *closest = nullptr;
		for (auto i = scene.spheres.begin(); i != scene.spheres.end(); i++)
		{
			Ray::Intersection sphereIntersect;
			if (ray.intersectSphere(sphereIntersect, (*i)->positionViewSpace, (*i)->radiusSquared) && sphereIntersect.t < intersection.t)
			{
				intersection = sphereIntersect;
				closest = (*i);
			}
		}
		
		Ogre::Vector3 barycentricCoords;
		for (auto iMesh = scene.meshes.begin(); iMesh != scene.meshes.end(); ++iMesh)
		{
			unsigned int triIndex = 0;
			for (auto i = (*iMesh)->getMesh()->indexBuffer.begin(); i != (*iMesh)->getMesh()->indexBuffer.end(); i+=3)
			{
				if (ray.intersectTriangleUpdate(intersection,
					(*iMesh)->vertexBufferVS[*i].position,
					(*iMesh)->triangleDataVS[triIndex]))
				{
					//closest = &(*iMesh)->triangleSurfaces[triIndex];
				}
				++triIndex;
			}
		}
		return closest;
	}
	/// Non-virtual method performance for a few miliseconds
	__forceinline bool _raytrace(Ogre::ColourValue &outColor, const Ray &ray, const Scene &scene, int recursionLevel) const
	{
		Ray::Intersection intersection;
		intersection.t = std::numeric_limits<float>::infinity();
		Surface *closest = rayIntersectUpdate(intersection, ray, scene, recursionLevel);
		if (closest)
		{
			FragmentProgramData fragData;
			fragData.position = ray.origin + intersection.t*ray.direction;
			closest->computeFragmentProgramData(fragData, intersection);
			(closest->getMaterial()->fn)(outColor, fragData, *closest->getMaterial(), scene, *this, recursionLevel);
			return true;
		}
		return false;
	}

public:
	~BruteForceRenderer() {}

	bool raytrace(Ogre::ColourValue &outColor, const Ray &ray, const Scene &scene, int recursionLevel) const
	{
		return _raytrace(outColor, ray, scene, recursionLevel);
	}

	bool raycastAny(const Ray &ray, const Scene &scene, float maxDist) const
	{
		Ray::Intersection intersection;
		intersection.t = maxDist;
		return rayIntersectUpdate(intersection, ray, scene, 9999) != nullptr;
	}

	void renderScene(std::vector<Ogre::ColourValue> &renderedImage, Scene &scene, const Camera &camera)
	{
		renderedImage.clear();
		renderedImage.resize(camera.getProjection().getWidth() * camera.getProjection().getHeight());

		scene.applyViewTransform(camera.getViewMatrix());

		int pixelIndex = 0;
		for (int y = camera.getProjection().getHeight()-1; y >= 0 ; y--)
		{
			for (int x = 0; x < camera.getProjection().getWidth(); x++)
			{
				Ogre::ColourValue col;
				float offset = 0.25f;
				_raytrace(col, camera.getProjection().pixelToRay((float)x, (float)y), scene, 0);
				renderedImage[pixelIndex] = col;
				pixelIndex++;
			}
		}
	}
};